﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
using Common.Utilities;
using Common.Utilities.Dispatcher;
using Workflows.Components;
using Workflows.Components.Entities.Distances;

namespace Workflows.Components.Entities.Relations.InferredAssociations
{
    public partial class ClusteringEditor : Form, IComponentEditor
    {
        #region fields
        private object _ActivityNode;
        private ClusteringComponent _ClusteringComponent;
        private Dictionary<string, IAttributeDistanceCalcutor> _Comparers;
        private Dictionary<string, IClusterCalculator> _ClusterCalcs;
        #endregion

        public ClusteringEditor()
        {
            InitializeComponent();
        }

        private void ClusteringEditor_Load(object sender, EventArgs e)
        {
            this._Comparers=new Dictionary<string, IAttributeDistanceCalcutor>();
            this._ClusterCalcs = new Dictionary<string, IClusterCalculator>();
            string binFolderPath = Assembly.GetExecutingAssembly().Location;
            binFolderPath = Path.GetDirectoryName(binFolderPath);
            DirectoryInfo dirInfo = new DirectoryInfo(binFolderPath);
            FileInfo[] assemFiles = dirInfo.GetFiles("*.dll", SearchOption.TopDirectoryOnly);
            foreach(FileInfo assemFileInfo in assemFiles)
            {
                if(assemFileInfo.Name.ToLower().StartsWith("discoverylogic") || 
                    assemFileInfo.Name.ToLower().StartsWith("workflow"))
                try
                {
                    Assembly assembly = Assembly.LoadFile(assemFileInfo.FullName);
                    Type[] types = assembly.GetTypes();
                    foreach(Type type in types)
                    {
                        if(type.GetInterface(typeof(IAttributeDistanceCalcutor).FullName) !=null)
                        {
                            IAttributeDistanceCalcutor comparer = (IAttributeDistanceCalcutor) Activator.CreateInstance(type);
                            this._Comparers.Add(type.FullName, comparer);
                        }
                        if(type.GetInterface(typeof(IClusterCalculator).FullName)!=null)
                        {
                            IClusterCalculator clusterCalculator = (IClusterCalculator) Activator.CreateInstance(type);
                            this._ClusterCalcs.Add(type.FullName,clusterCalculator);
                        }
                    }
                }
                catch (Exception)
                {
                }
            }

            this.cbo_Comparers.Items.Clear();
            foreach(string comparerName in this._Comparers.Keys)
            {
                this.cbo_Comparers.Items.Add(comparerName);
            }
            this.cbo_ClusteringMethod.Items.Clear();
            foreach(string clusterCalcName in this._ClusterCalcs.Keys)
            {
                this.cbo_ClusteringMethod.Items.Add(clusterCalcName);
            }
        }

        #region IComponentEditor Members

        public event UpdateComponentSettings UpdateComponentSettingEvent;

        public object OwnerActivityNode
        {
            get { return _ActivityNode; }
            set { _ActivityNode=value; }
        }

        public Type WorkflowComponentType
        {
            get { return typeof(ClusteringComponent); }
        }

        public void LoadSettings(GlobalVariable[] globalVars, WorkflowComponentBase[] fromComponents,
                                 object activityNode, WorkflowComponentBase wfComponent)
        {
            this._ActivityNode = activityNode;
            this._ClusteringComponent = (ClusteringComponent) wfComponent;

            // src 
            this.txt_PartitionFilePath.Text = this._ClusteringComponent.EntityPartitionFilePath;
            if(!string.IsNullOrEmpty(this._ClusteringComponent.EntityPartitionFilePath))
                this.ck_PartitionProvider.Checked = true;
            else
                this.ck_PartitionProvider.Checked = false;
            this.txt_ConnStr.Text = this._ClusteringComponent.ConnStr;
            this.cbo_DbProvider.SelectedIndex = (int) this._ClusteringComponent.DbProvider;
            this.rt_SQL.Text = this._ClusteringComponent.SelectSql;
            this.cbo_IdAttribute.Items.Clear();
            if(!string.IsNullOrEmpty(this.txt_ConnStr.Text) && !string.IsNullOrEmpty(this.rt_SQL.Text))
            {
                TableColumn[] cols = DbUtil.GetTableColumns(
                    this._ClusteringComponent.DbProvider,
                    this.txt_ConnStr.Text, this.rt_SQL.Text);
                if(cols != null && cols.Length>0)
                {
                    foreach(TableColumn col in cols)
                    {
                        this.cbo_IdAttribute.Items.Add(col.ColumnName);
                    }
                    if(!string.IsNullOrEmpty(this._ClusteringComponent.EntityIDFieldName))
                    {
                        for(int i=0;i<this.cbo_IdAttribute.Items.Count;i++)
                        {
                            if(this.cbo_IdAttribute.Items[i].ToString()==this._ClusteringComponent.EntityIDFieldName)
                            {
                                this.cbo_IdAttribute.SelectedIndex = i;
                                break;
                            }
                        }
                    }
                }
            }

            // comparers 
            this.dv_Attributes.Rows.Clear();
            if(this._ClusteringComponent.ComparableAttributeMetaDatas !=null && this._ClusteringComponent.ComparableAttributeMetaDatas.Count>0)
            {
                foreach (AttributeMetaData attrMetaData in this._ClusteringComponent.ComparableAttributeMetaDatas)
                {
                    DataGridViewRow dr = new DataGridViewRow();
                    DataGridViewTextBoxCell cellAttrName = new DataGridViewTextBoxCell();
                    if(attrMetaData.AttributeType==AttributeType.CompositeAttribute)
                    {
                        string attrNameBindings = string.Empty;
                        string[] argNames=new string[attrMetaData.ArgNameAttrNameBindings.Count];
                        attrMetaData.ArgNameAttrNameBindings.Keys.CopyTo(argNames,0);
                        for(int i=0;i<argNames.Length;i++)
                        {
                            attrNameBindings += argNames[i] + "=" + attrMetaData.ArgNameAttrNameBindings[argNames[i]];
                            if (i < argNames.Length - 1)
                                attrNameBindings += ",";
                        }
                        cellAttrName.Value = attrNameBindings;
                    }
                    else
                    {
                        cellAttrName.Value = attrMetaData.AttributeName;
                    }
                    dr.Cells.Add(cellAttrName);
                    DataGridViewComboBoxCell cellAttrType = new DataGridViewComboBoxCell();
                    for(int i=0;i<=4;i++)
                    {
                        AttributeType attrType = (AttributeType) i;
                        cellAttrType.Items.Add(attrType.ToString());
                    }
                    cellAttrType.Value = attrMetaData.AttributeType.ToString();
                    dr.Cells.Add(cellAttrType);
                    DataGridViewTextBoxCell cellPosW = new DataGridViewTextBoxCell();
                    cellPosW.Value = attrMetaData.PositiveMatchWeight;
                    dr.Cells.Add(cellPosW);
                    DataGridViewTextBoxCell cellNegW = new DataGridViewTextBoxCell();
                    cellNegW.Value = attrMetaData.NegativeMatchWeight;
                    dr.Cells.Add(cellNegW);
                    DataGridViewComboBoxCell cellComparer = new DataGridViewComboBoxCell();
                    foreach(string comparerName in this._Comparers.Keys)
                    {
                        cellComparer.Items.Add(comparerName);
                    }
                    cellComparer.Value = attrMetaData.ComparerTypeName;
                    dr.Cells.Add(cellComparer);

                    DataGridViewCheckBoxCell cellExternal = new DataGridViewCheckBoxCell();
                    cellExternal.Value = attrMetaData.IsExternalAttribute;
                    dr.Cells.Add(cellExternal);

                    DataGridViewComboBoxCell cellDbProvider = new DataGridViewComboBoxCell();
                    for (int i = 0; i <= 1; i++)
                    {
                        cellDbProvider.Items.Add(((DataProviderType)i).ToString());
                    }
                    cellDbProvider.Value = attrMetaData.ExternalAttributeDbProvider.ToString();
                    dr.Cells.Add(cellDbProvider);

                    DataGridViewTextBoxCell cellConnStr = new DataGridViewTextBoxCell();
                    cellConnStr.Value = attrMetaData.ExternalAttributeConnStr;
                    dr.Cells.Add(cellConnStr);

                    DataGridViewTextBoxCell cellSql = new DataGridViewTextBoxCell();
                    cellSql.Value = attrMetaData.ExternalAttributeSelectSql;
                    dr.Cells.Add(cellSql);

                    this.dv_Attributes.Rows.Add(dr);
                }
            }

            // threshold
            if (this._ClusteringComponent.ClusterSizeThreshold < int.MaxValue)
            {
                this.ck_ClusterSize.Checked = true;
                this.num_ClusterSize.Value = this._ClusteringComponent.ClusterSizeThreshold;
            }
            else
            {
                this.ck_ClusterSize.Checked = false;
            }
            if (this._ClusteringComponent.ClusterDiameterThreshold < double.PositiveInfinity)
            {
                this.ck_ClusterDistance.Checked = true;
                this.num_ClusterDistance.Value = (decimal) this._ClusteringComponent.ClusterDiameterThreshold;
            }
            else
            {
                this.ck_ClusterDistance.Checked = false;
            }
            if (this._ClusteringComponent.EntityMembershipThreshold > 0)
            {
                this.ck_Membership.Checked = true;
                this.num_Membership.Value = (decimal) this._ClusteringComponent.EntityMembershipThreshold;
            }
            else
            {
                this.ck_Membership.Checked = false;
            }

            // cluster 
            if (this.cbo_ClusteringMethod.Items.Count > 0 &&
                (!string.IsNullOrEmpty(this._ClusteringComponent.ClusterCalculatorTypeName)))
            {
                for(int i=0;i<this.cbo_ClusteringMethod.Items.Count;i++)
                {
                    if(this.cbo_ClusteringMethod.Items[i].ToString()==this._ClusteringComponent.ClusterCalculatorTypeName)
                    {
                        this.cbo_ClusteringMethod.SelectedIndex = i;
                        break;
                    }
                }
            }

            // tgt 
            this.cbo_TgtDbProvider.SelectedIndex = (int) this._ClusteringComponent.TgtDbProvider;
            this.txt_TgtConnStr.Text = this._ClusteringComponent.TgtConnStr;
            this.cbo_InstanceTable.Items.Clear();
            this.cbo_MappingTable.Items.Clear();
            if(!string.IsNullOrEmpty(this.txt_TgtConnStr.Text))
            {
                string[] tblNames = DbUtil.GetTableNames(
                    this._ClusteringComponent.TgtDbProvider, string.Empty, this.txt_TgtConnStr.Text);
                if(tblNames !=null && tblNames.Length>0)
                {
                    foreach(string tblName in tblNames)
                    {
                        this.cbo_InstanceTable.Items.Add(tblName);
                        this.cbo_MappingTable.Items.Add(tblName);
                    }
                }

                this.SelectComboBoxItem(this.cbo_InstanceTable, this._ClusteringComponent.ClusterInstanceTableName);
                this.SelectComboBoxItem(this.cbo_MappingTable, this._ClusteringComponent.ClusterMembershipTableName);

                this.SelectComboBoxItem(this.cbo_InstanceClusterIDField, this._ClusteringComponent.ClusterInstanceClusterIDFieldName);
                this.SelectComboBoxItem(this.cbo_MappingTableCollapseIDField, this._ClusteringComponent.ClusterMemberClusterIDFieldName);
                this.SelectComboBoxItem(this.cbo_MappingTableEntityIDField, this._ClusteringComponent.ClusterMemberEntityIDFieldName);
                this.SelectComboBoxItem(this.cbo_MappingTableScoreField, this._ClusteringComponent.ClusterMemberScoreFieldName);
                this.SelectComboBoxItem(this.cbo_MappingTableMembershipField, this._ClusteringComponent.ClusterMemberMembershipFieldName);
            }
        }

        private void SelectComboBoxItem(ComboBox cboBox, string value)
        {
            if(cboBox.Items.Count>0 && !string.IsNullOrEmpty(value))
            {
                for(int i=0;i<cboBox.Items.Count;i++)
                {
                    if(cboBox.Items[i].ToString()==value)
                    {
                        cboBox.SelectedIndex = i;
                        break;
                    }
                }
            }
        }

        public void SaveSettings()
        {
            if(this.ck_PartitionProvider.Checked && !string.IsNullOrEmpty(this.txt_PartitionFilePath.Text))
               this._ClusteringComponent.EntityPartitionFilePath = this.txt_PartitionFilePath.Text;
            else
                this._ClusteringComponent.EntityPartitionFilePath = string.Empty;
            this._ClusteringComponent.DbProvider = (DataProviderType) this.cbo_DbProvider.SelectedIndex;
            this._ClusteringComponent.ConnStr = this.txt_ConnStr.Text;
            this._ClusteringComponent.SelectSql = this.rt_SQL.Text;
            this._ClusteringComponent.EntityIDFieldName = this.cbo_IdAttribute.Text;

            // comparers 
            this._ClusteringComponent.ComparableAttributeMetaDatas = new List<AttributeMetaData>();
            foreach(DataGridViewRow dr in this.dv_Attributes.Rows)
            {
                if (!dr.IsNewRow && dr.Cells[0].Value != null)
                {
                    AttributeType attrType = AttributeType.NativeAttribute;
                    for(int i=0;i<=4;i++)
                    {
                        if(dr.Cells[1].Value.ToString()== ((AttributeType)i).ToString())
                        {
                            attrType = (AttributeType) i;
                            break;
                        }
                    }
                    string attrName = dr.Cells[0].Value.ToString();
                    double posW = double.Parse(dr.Cells[2].Value.ToString());
                    double negW = double.Parse(dr.Cells[3].Value.ToString());
                    string comparerTypeName = dr.Cells[4].Value.ToString();
                    string assemblyName = this._Comparers[comparerTypeName].GetType().Assembly.GetName().Name;
                    bool isExternalAttr = (bool)dr.Cells[5].Value;
                    DataProviderType dbProvider = DataProviderType.MSSQL;
                    if (((string)dr.Cells[6].Value) == DataProviderType.Oracle.ToString())
                        dbProvider = DataProviderType.Oracle;
                    string connStr = dr.Cells[7].ToString();
                    string sql = dr.Cells[8].Value.ToString();

                    Dictionary<string, string> comparerSettings = new Dictionary<string, string>();
                    if (dr.Cells[9].Value != null && dr.Cells[9].Value.ToString().Length > 0)
                    {
                        string[] settingStrs = StringUtil.Split(dr.Cells[9].Value.ToString(), ",");
                        foreach (string settingStr in settingStrs)
                        {
                            string[] nameValPair = StringUtil.Split(settingStr, "=");
                            if (nameValPair.Length == 2)
                            {
                                comparerSettings.Add(nameValPair[0], nameValPair[1]);
                            }
                        }
                    }

                    if(attrType==AttributeType.CompositeAttribute)
                    {
                        string[] argAttrpairs = StringUtil.Split(attrName, ",");
                        Dictionary<string, string> argAttrBindings = new Dictionary<string, string>();
                        foreach (string argAttrpair in argAttrpairs)
                        {
                            string[] pair = StringUtil.Split(argAttrpair, "=");
                            if(pair.Length==2)
                            {
                                argAttrBindings.Add(pair[0],pair[1]);
                            }
                        }

                        
                        AttributeMetaData attrMetaData =
                            new AttributeMetaData(argAttrBindings,
                                                  assemblyName, comparerTypeName,
                                                  comparerSettings, posW, negW,
                                                  isExternalAttr, dbProvider, connStr, sql);
                        this._ClusteringComponent.ComparableAttributeMetaDatas.Add(attrMetaData);
                    }
                    else
                    {
                        AttributeMetaData attributeMetaData =
                            new AttributeMetaData(
                                attrName, attrType, assemblyName, comparerTypeName,
                                comparerSettings, posW, negW,
                                isExternalAttr, dbProvider, connStr, sql);
                        this._ClusteringComponent.ComparableAttributeMetaDatas.Add(attributeMetaData);
                    }
                    
                }
            }
            
            // threshold
            if(this.ck_ClusterSize.Checked)
                this._ClusteringComponent.ClusterDiameterThreshold = (double)this.num_ClusterDistance.Value;
            else
                this._ClusteringComponent.ClusterDiameterThreshold = double.PositiveInfinity;
            if(this.ck_ClusterSize.Checked)
                this._ClusteringComponent.ClusterSizeThreshold = (int) this.num_ClusterSize.Value;
            else
                this._ClusteringComponent.ClusterSizeThreshold = int.MaxValue;
            if (this.ck_Membership.Checked)
                this._ClusteringComponent.EntityMembershipThreshold = (double)this.num_Membership.Value;
            else
                this._ClusteringComponent.EntityMembershipThreshold = 0;

            // clustering 
            this._ClusteringComponent.ClusterCalculatorTypeName = this.cbo_ClusteringMethod.SelectedItem.ToString();

            // tgt
            this._ClusteringComponent.TgtDbProvider = (DataProviderType) this.cbo_TgtDbProvider.SelectedIndex;
            this._ClusteringComponent.TgtConnStr = this.txt_TgtConnStr.Text;
            this._ClusteringComponent.ClusterInstanceTableName = this.cbo_InstanceTable.Text;
            this._ClusteringComponent.ClusterMembershipTableName = this.cbo_MappingTable.Text;
            this._ClusteringComponent.ClusterInstanceClusterIDFieldName = this.cbo_InstanceClusterIDField.Text;
            this._ClusteringComponent.ClusterMemberClusterIDFieldName = this.cbo_MappingTableCollapseIDField.Text;
            this._ClusteringComponent.ClusterMemberEntityIDFieldName = this.cbo_MappingTableEntityIDField.Text;
            this._ClusteringComponent.ClusterMemberScoreFieldName = this.cbo_MappingTableScoreField.Text;
            this._ClusteringComponent.ClusterMemberMembershipFieldName = this.cbo_MappingTableMembershipField.Text;

        }

        public TestResult Test()
        {
            throw new NotImplementedException();
        }

        #endregion

        private void btn_Test_Click(object sender, EventArgs e)
        {

        }

        private void btn_OK_Click(object sender, EventArgs e)
        {
            this.SaveSettings();
            if (this.UpdateComponentSettingEvent != null)
                this.UpdateComponentSettingEvent(this._ActivityNode, this._ClusteringComponent);

            this.Close();
        }

        private void btn_Cancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        private void btn_BrowseDataSource_Click(object sender, EventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "Input entities (*.xml)|*.xml|All files(*.*)|*.*";
            if (dlg.ShowDialog() == DialogResult.OK)
            {
                this.txt_PartitionFilePath.Text = dlg.FileName;
            }
        }

        private void ck_ClusterSize_CheckedChanged(object sender, EventArgs e)
        {
            if(this.ck_ClusterSize.Checked)
                this.num_ClusterSize.Enabled = true;
            else
                this.num_ClusterSize.Enabled = false;
        }

        private void ck_ClusterDistance_CheckedChanged(object sender, EventArgs e)
        {
            if(this.ck_ClusterDistance.Checked)
                this.num_ClusterDistance.Enabled = true;
            else
                this.num_ClusterDistance.Enabled = false;
        }

        private void ck_Membership_CheckedChanged(object sender, EventArgs e)
        {
            if(this.ck_Membership.Checked)
                this.num_Membership.Enabled = true;
            else
                this.num_Membership.Enabled = false;
        }

   
    }
}
